//: concurrency/NotifyVsNotifyAll.java
package concurrency;

import java.util.concurrent.*;
import java.util.*;

class Blocker
{
    synchronized void waitingCall()
    {
        try
        {
            while (!Thread.interrupted())
            {
                wait();
                System.out.print(Thread.currentThread() + " ");
            }
        }
        catch (InterruptedException e)
        {
            // OK to exit this way
        }
    }
    
    synchronized void prod()
    {
        notify();
    }
    
    synchronized void prodAll()
    {
        notifyAll();
    }
}

class Task implements Runnable
{
    static Blocker blocker = new Blocker();
    
    public void run()
    {
        blocker.waitingCall();
    }
}

class Task2 implements Runnable
{
    // A separate Blocker object:
    static Blocker blocker = new Blocker();
    
    public void run()
    {
        blocker.waitingCall();
    }
}

public class NotifyVsNotifyAll
{
    public static void main(String[] args)
        throws Exception
    {
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++)
            exec.execute(new Task());
        exec.execute(new Task2());
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask()
        {
            boolean prod = true;
            
            public void run()
            {
                if (prod)
                {
                    System.out.print("\nnotify() ");
                    Task.blocker.prod();
                    prod = false;
                }
                else
                {
                    System.out.print("\nnotifyAll() ");
                    Task.blocker.prodAll();
                    prod = true;
                }
            }
        }, 400, 400); // Run every .4 second
        TimeUnit.SECONDS.sleep(5); // Run for a while...
        timer.cancel();
        System.out.println("\nTimer canceled");
        TimeUnit.MILLISECONDS.sleep(500);
        System.out.print("Task2.blocker.prodAll() ");
        Task2.blocker.prodAll();
        TimeUnit.MILLISECONDS.sleep(500);
        System.out.println("\nShutting down");
        exec.shutdownNow(); // Interrupt all tasks
    }
} /*
   * Output: (Sample) notify() Thread[pool-1-thread-1,5,main] notifyAll() Thread[pool-1-thread-1,5,main]
   * Thread[pool-1-thread-5,5,main] Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-3,5,main]
   * Thread[pool-1-thread-2,5,main] notify() Thread[pool-1-thread-1,5,main] notifyAll() Thread[pool-1-thread-1,5,main]
   * Thread[pool-1-thread-2,5,main] Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-4,5,main]
   * Thread[pool-1-thread-5,5,main] notify() Thread[pool-1-thread-1,5,main] notifyAll() Thread[pool-1-thread-1,5,main]
   * Thread[pool-1-thread-5,5,main] Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-3,5,main]
   * Thread[pool-1-thread-2,5,main] notify() Thread[pool-1-thread-1,5,main] notifyAll() Thread[pool-1-thread-1,5,main]
   * Thread[pool-1-thread-2,5,main] Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-4,5,main]
   * Thread[pool-1-thread-5,5,main] notify() Thread[pool-1-thread-1,5,main] notifyAll() Thread[pool-1-thread-1,5,main]
   * Thread[pool-1-thread-5,5,main] Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-3,5,main]
   * Thread[pool-1-thread-2,5,main] notify() Thread[pool-1-thread-1,5,main] notifyAll() Thread[pool-1-thread-1,5,main]
   * Thread[pool-1-thread-2,5,main] Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-4,5,main]
   * Thread[pool-1-thread-5,5,main] Timer canceled Task2.blocker.prodAll() Thread[pool-1-thread-6,5,main] Shutting down
   */// :~
